﻿using System;
using System.Collections.Generic;
using System.IO;

using RexToy.Xml;
using RexToy.Collections;
using RexToy.Resources;
using RexToy.ExpressionLanguage;
using RexToy.UI.WinForm.ViewModel;

namespace RexToy.UI.WinForm.Pmlx
{
    public class PmlxDoc : IDisposable
    {
        private XDoc _doc;
        private PmlxDoc(XDoc doc)
        {
            _doc = doc;
        }

        public void Initialize()
        {
            LoadStyleSheets();
            LoadStaticResources();
            LoadViewModels();
        }

        public static PmlxDoc Load(string file)
        {
            XDoc doc = XDoc.LoadFromFile(file);
            return new PmlxDoc(doc);
        }

        public static PmlxDoc Load(Stream stream)
        {
            XDoc doc = XDoc.LoadFromStream(stream);
            return new PmlxDoc(doc);
        }

        private ExpressionLanguageEngine _el;
        public ExpressionLanguageEngine ELEngine
        {
            get
            {
                //Note:Not lock here, UI setup is used in single thread mode usually.
                if (_el == null)
                {
                    _el = ExpressionLanguageEngine.CreateEngine();

                    _el.Assign(PmlxConstant.CTX_RES, this._staticResources);
                    _el.Assign(PmlxConstant.CTX_MODELS, this._viewModels);
                }
                return _el;
            }
        }

        private StyleCollection _styles;
        public StyleCollection Styles
        {
            get { return _styles; }
        }

        private void LoadStyleSheets()
        {
            _styles = new StyleCollection();

            foreach (var x in _doc.Children)
            {
                if (x.LocalName == PmlxConstant.STYLE_SHEET && this.GetNamespaceOfPrefix(x.Prefix) == PmlxConstant.STD_X_4_0)
                {
                    if (x.Attributes.Count != 1)
                        ExceptionHelper.ThrowInvalidStyleSheet();
                    if (x.Attributes[0].LocalName != PmlxConstant.URI)
                        ExceptionHelper.ThrowInvalidStyleSheet();

                    string uri = x.Attributes[0].GetStringValue();
                    ITargetLocator tl = LocatorFactory.Create(uri);
                    using (Stream stream = tl.GetStream(true))
                    {
                        XDoc styleDoc = XDoc.LoadFromStream(stream);

                        var xStyles = styleDoc.NavigateToList(PmlxConstant.STYLE_NODE);
                        foreach (var xStyle in xStyles)
                        {
                            string key = xStyle.GetStringValue(PmlxConstant.NAME_ATTR);

                            var xSet = xStyle.NavigateToSingle(PmlxConstant.SET);
                            _styles[key] = new Style(xSet);
                        }
                    }
                }
            }
        }

        private StaticResourceCollection _staticResources;
        private void LoadStaticResources()
        {
            foreach (var x in this.RootNode.ChildNodes)
            {
                if (x.LocalName == PmlxConstant.STATIC_RESOURCE && this.GetNamespaceOfPrefix(x.Prefix) == PmlxConstant.STD_X_4_0)
                {
                    _staticResources = new StaticResourceCollection();
                    foreach (PmlxNode node in x.ChildNodes)
                    {
                        IStaticResource res = node.LoadResourceElement();
                        _staticResources.AddResource(res.Key, res.Target);
                    }
                }
            }
        }

        private Dictionary<string, ViewModelBase> _viewModels;
        private void LoadViewModels()
        {
            _viewModels = new Dictionary<string, ViewModelBase>();
            foreach (var x in this.RootNode.ChildNodes)
            {
                if (x.LocalName == PmlxConstant.VIEW_MODEL && this.GetNamespaceOfPrefix(x.Prefix) == PmlxConstant.STD_X_4_0)
                {
                    foreach (var child in x.ChildNodes)
                    {
                        ViewModelBase vm = Activator.CreateInstance(child.MakeType()) as ViewModelBase;
                        IReflector r = Reflector.Bind(vm);

                        foreach (var attr in child.Attributes)
                            attr.ApplyValue(r);

                        _viewModels.Add(vm.Key, vm);
                    }
                }
            }
        }

        public PmlxNode RootNode
        {
            get
            {
                return new PmlxNode(_doc, this);
            }
        }

        public string GetNamespaceOfPrefix(string prefix)
        {
            return _doc.Node.GetNamespaceOfPrefix(prefix);
        }

        private bool _disposed;
        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
                return;

            if (disposing)
            {
                if (_staticResources != null)
                    _staticResources.Dispose();
            }

            _disposed = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        ~PmlxDoc()
        {
            Dispose(false);
        }
    }
}
